package van.mvn;

import com.intellij.concurrency.JobScheduler;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import org.jetbrains.annotations.Nullable;
import van.index.Indexer;
import van.index.MavenRepositoryIndexManager;

import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeSelectionModel;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
import java.io.File;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class MavenRepositorySearchDialog extends DialogWrapper {
    private JPanel contentPane;
    private JTextField searchText;
    private JTree tree1;
    private JLabel selectedItem;

    private String queryText;

    private MavenRepositoryIndexManager mavenRepositoryIndexManager;
    SearchJob searchJob;


    public MavenRepositorySearchDialog(Project project, boolean canBeParent, String queryText) {
        super(project, canBeParent);
        this.queryText = queryText;
        mavenRepositoryIndexManager = ServiceManager.getService(MvnProjectComponent.class).getMavenRepositoryIndexManager();
        init();
    }

    private void createUIComponents() {
        tree1 = new JTree(new DefaultMutableTreeNode());
        tree1.getSelectionModel().setSelectionMode
                (TreeSelectionModel.SINGLE_TREE_SELECTION);
        tree1.setRootVisible(false);
    }

    @Nullable
    protected JComponent createCenterPanel() {
        searchText.addKeyListener(new KeyListener() {
            public void keyTyped(KeyEvent e) {
            }

            public void keyPressed(KeyEvent e) {
            }

            public void keyReleased(KeyEvent e) {
                scheduleSearch(searchText.getText());
            }
        });

        tree1.addTreeSelectionListener(new TreeSelectionListener() {
            public void valueChanged(TreeSelectionEvent e) {
                Object o = getData();

                Indexer.FileInfo f = getSelectedFileInfo(o);

                updateStatus(f.name + " " + f.size + " " + f.date);
            }
        });

        scheduleSearch(queryText);

        return contentPane;
    }

    private Indexer.FileInfo getSelectedFileInfo(Object element) {
        if (element instanceof Indexer.ArtifactInfo) {
            return (Indexer.FileInfo) ((Indexer.ArtifactInfo) element).files.iterator().next();
        }
        return (Indexer.FileInfo) element;
    }

    public Object getData() {
        Object o = tree1.getLastSelectedPathComponent();
        if (o instanceof ArtifectTreeNode) {
            return ((ArtifectTreeNode) o).getUserObject();
        } else if (o instanceof FileTreeNode) {
            return ((FileTreeNode) o).getUserObject();
        }
        return null;
    }

    private void scheduleSearch(String query) {
        if (query != null && query.length() > 0) {
            if (searchJob == null) {
                File[] indexes = mavenRepositoryIndexManager.getIndexes();
                searchJob = new SearchJob(Indexer.JAR_NAME, indexes, this);
            }

            searchJob.setQuery(query.toLowerCase());
            if (!searchJob.isRunning()) {
                searchJob.schedule();
            }
        }
    }

    private void setSearchResult(Map result) {
        DefaultMutableTreeNode root = new DefaultMutableTreeNode();
        for (Object o : result.values()) {
            if (o instanceof Indexer.ArtifactInfo) {
                Indexer.ArtifactInfo a = (Indexer.ArtifactInfo) o;
                ArtifectTreeNode artifectTreeNode = new ArtifectTreeNode(a);

                for (Object file : a.files) {
                    if (file instanceof Indexer.FileInfo) {
                        Indexer.FileInfo f = (Indexer.FileInfo) file;
                        artifectTreeNode.add(new FileTreeNode(f));
                    }
                }

                root.add(artifectTreeNode);
            }
        }
        tree1.setModel(new DefaultTreeModel(root));

    }

    private void updateStatus(String s) {
        selectedItem.setText(s);
    }

    protected void init() {
        super.init();
    }


    private class SearchJob {
        final MavenRepositorySearchDialog dialog;
        final Indexer indexer;

        private File[] indexes;

        private String query;

        private String field;

        private boolean running = false;

        public SearchJob(String field, File[] indexes, MavenRepositorySearchDialog mavenRepositorySearchDialog) {
            this.dialog = mavenRepositorySearchDialog;
            this.indexes = indexes;
            this.indexer = new Indexer();
            this.field = field;
        }

        public void setQuery(String query) {
            this.query = query;
        }

        public boolean isRunning() {
            return running;
        }

        public void schedule() {
            JobScheduler.getScheduler().schedule(new Runnable() {
                public void run() {
                    running = true;
                    while (query != null) {
                        String activeQuery = query;
                        query = null;
                        try {
                            Map res = indexer.search(indexes, activeQuery, field);
                            setResult("Result for: " + activeQuery, res);

                        } catch (final RuntimeException ex) {
                            setResult("Search error: " + ex.toString(), Collections.EMPTY_MAP);
                        } catch (final Exception ex) {
                            setResult("Search error: " + ex.getMessage(), Collections.EMPTY_MAP);
                        }
                    }
                    running = false;
                }

                private void setResult(final String s, final Map result) {
                    ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
                        public void run() {
                            dialog.updateStatus(s);
                            dialog.setSearchResult(result);
                        }
                    });
                }
            }, 1000L, TimeUnit.MILLISECONDS);
        }
    }

    private class ArtifectTreeNode extends DefaultMutableTreeNode {
        public ArtifectTreeNode(Indexer.ArtifactInfo o) {
            this.setUserObject(o);
        }

        public boolean getAllowsChildren() {
            return true;
        }

        public boolean isLeaf() {
            return false;
        }

        public String toString() {
            Indexer.ArtifactInfo a = (Indexer.ArtifactInfo) this.getUserObject();
            return (a.className == null ? "" : a.className + "   " + a.packageName + "   ") + a.group + "   " + a.artifact;
        }

    }

    private class FileTreeNode extends DefaultMutableTreeNode {

        public FileTreeNode(Indexer.FileInfo o) {
            this.setUserObject(o);
        }

        public boolean getAllowsChildren() {
            return false;
        }

        public boolean isLeaf() {
            return true;
        }

        public String toString() {
            Indexer.FileInfo f = (Indexer.FileInfo) this.getUserObject();
            return f.version + " - " + f.name + " - " + f.size + " - " + f.date + " [" + f.repository + "]";
        }
    }
}
